#include "declarations.hpp"
#include "track_database.hpp"
#include "space_fixes.hpp"
#include "maintenance_help.hpp"
#include "query_extraction.hpp"
#include "individualize.hpp"
#include "progress_indicator.hpp"
#include "make_output_files.hpp"
#include "arithmetic_related.hpp"

inline void post_append( pair<strings,strings>& p1, const pair<strings,strings>& p2 ){
	p1.first.insert( p1.first.end(), p2.first.begin(), p2.first.end() );
	p1.second.insert( p1.second.end(), p2.second.begin(), p2.second.end() );
}

int main( int argc, char * argv[] ){

	// Taking into account user choices..
	int num_param2 = 0; // ..for output #2.

	char ABC_CUSTOM_GLOBAL;
	cout << "Choose A to specify a GLOBAL value (FOR ALL FILE/S) "
			<< "or \nB to keep to the DEFAULT value (ZERO) "
			<< "or \nC to INDIVIDUALLY assign the value for each log "
			<< "\n[of the numerical parameter for output #2]: ";
	cin >> ABC_CUSTOM_GLOBAL;

	while( ABC_CUSTOM_GLOBAL != 'A'
		and ABC_CUSTOM_GLOBAL != 'B'
		and ABC_CUSTOM_GLOBAL != 'C' ){
			cout << "\nPlease enter valid option: ";
			cin >> ABC_CUSTOM_GLOBAL;
	}

	cout << endl;

	if( ABC_CUSTOM_GLOBAL == 'A' ){
		cout << "Please enter the desired (\"global\") value "
				<< "of the numerical parameter for output #2: ";
		cin >> num_param2; cout << endl;
	}

	// Default file to parse is <sample.log> (originally
	// supplied as <pg.log>) if none specified at all...
	char filename[384] = "sample.log";
	int argument = 1; do {

		// Obtain filename from command line input.
		if( argc > 1 ) strcpy( filename, argv[ argument ] );

		// Count lines in the specified log
		// file (useful to track progress).
		// --------------------------------

		ifstream glance_file( filename );
		if( !glance_file.is_open() ){
			cerr << "Unable to open file #" << argument << "!" << endl;
			assert( glance_file.is_open() );
		}

		int size_init = count(
				istreambuf_iterator<char>( glance_file ),
				istreambuf_iterator<char>(), '\n' );

		glance_file.close();

		// No progress is happening, so we must keep
		// "len" (first parameter) as 0 in this case.
		note_progress( 0, true, size_init/1000.0 );

		// --------------------------------

		if( argc > 2 ){ if( argument > 1 ) cout << endl;
			cout << "File #" << argument << ": " << filename << endl; }

		if( ABC_CUSTOM_GLOBAL == 'C' ){
			cout << "Please enter the desired value of the"
					<< " numerical parameter for output #2: ";
			cin >> num_param2;
		}

		// Open the specified log file.
		ifstream log( filename );
		if( !log.is_open() ){
			cerr << "Unable to open file!" << endl;
			assert( log.is_open() );
		}

		// Reset database "history" before beginning to parse the next
		// log file; more completely explained in <track_database.hpp>.
		intellifind_db( blank, true );

		return_map( 4, true );

		// Number of lines read in that iteration
		// (merely to indicate overall progress).
		unsigned lines_covered = 0, reached;
		// Run through all "statements" sequentially and
		// process them as required in a way easy to debug
		// (looking through the entire process piecewise).
		for( ; log.good(); reached = note_progress( lines_covered ) ){

			// First we get preprocessed
			// queries from the log file.
			string log_entry = get_next( log, lines_covered );
			assert( disambiguate( log_entry ) );
			strings extracted = get_all_select( log_entry );
			for( int i = 0, l = extracted.size(); i < l; i++ )
				make_parsing_ready( extracted.at( i ) );

			// Next, we keep (or update) the record
			// of the current database (cdb) in use.
			string cdb = intellifind_db( log_entry );

			// Next we find the table(s) involved in
			// the log entry (mostly a single query)
			// while also updating the various counts
			// we need to get the overall stats...
			pair<strings,strings> overall_table_list;
			for( int i = 0, l = extracted.size(); i < l; i++ ){

				string gradually_truncated = extracted.at( i );

				string temp_portion;
				static const RE from_portion_re = in_between( string( "FROM" ), ORSQK );
				static const RE joinOnRegEx0 = in_between( string( "J#O#I#N" ), string( "ON" ) );
				if( from_portion_re.PartialMatch( gradually_truncated, &temp_portion ) )
					post_append( overall_table_list, handle_aliases( temp_portion ) );
				while( joinOnRegEx0.PartialMatch( gradually_truncated, &temp_portion ) ){
					replace_first( gradually_truncated, temp_portion, "J#O#I#N#" );
					post_append( overall_table_list, handle_aliases( temp_portion ) );
				}

				separate_counts( gradually_truncated, cdb, overall_table_list );

			}

			count( log_entry, cdb, overall_table_list, BLIND_MAP );

		} log.close();

		bool indicate = true, find = false;
		if( reached != 0 and indicate ) std::cout << std::endl;
		if( reached != 0 and find ) std::cerr << std::endl;

		if( argument == 1 ) print_outputs( num_param2, false );
		else print_outputs( num_param2, true );

		list_unknown_functions( argument );

	} while( ++argument < argc );

	return 0;

}
